skit.define("skit.platform.Controller:js", ["skit.platform.iter:js","skit.platform.util:js","skit.platform.util:js"], function() { return ((function(skit_platform_iter,skit_platform_util,skit_platform_util) { var module = {exports: {}}; var defined = null; function define() { for (var i = 0; i < arguments.length; i++) { if (typeof arguments[i] == 'function') { defined = arguments[i](); break; } } } define.amd = true; var result = (function skit_platform_Controller_js() {'use strict'; /** * @license * (c) 2014 Cluster Labs, Inc. https://cluster.co/ * License: MIT */ /** @ignore */ var iter = skit_platform_iter; /** @ignore */ var util = skit_platform_util; /** @ignore */ var util = skit_platform_util; /** * Render a page for a skit URL path. * @class * @name Controller */ var Controller = util.createClass(/** @lends Controller# */{ /** * Constructor function should not be called directly and should not be * overridden. */ __init__: function(params) { this.params = params; }, /** * Loads any necessary data from the backend API and handles any navigation * that should occur before render time. Only called once, typically on the * server side. * * At this point, there is no window or document available, so this method * should only call into a backend API, or read data supplied by other * skit modules. window/document access should wait until the call to * __ready__(). * * Properties assigned to {this} during this stage, eg. this.data = foo, * will be serialized to JSON and re-assigned to this controller object on * the client side. * * @param onLoaded {Function} A callback to call when you are done preloading * content from the backend. */ __preload__: function(onLoaded) { onLoaded(); }, /** * Called on the server and client after preload has completed. You can use * this method like a constructor to set up any state for the rest of the object to use. * * IMPORTANT: You will not have access to window or document at this point, * since this might be running on the server. Any window/document access * should start in __ready__(). */ __load__: function() {}, /** * Returns the title for this page. If this is a parent constructor, you will * also get the title provided by the child controller as an argument. * * IMPORTANT: You will not have access to window or document at this point, * since this might be running on the server. Any window/document access * should start in __ready__(). * * @param {string=} childTitle The title generated by a child controller, if * this is a parent controller and the child controller generated a * title. * @return {string} The title of this page. */ __title__: function(childTitle) { return childTitle; }, /** * Returns additional content to put in the head of this page, which would * typically be things like meta tags (hence the name). If this is a parent * controller, the argument will be the string generated by the child * controller, which you should include in your response. * * IMPORTANT: You will not have access to window or document at this point, * since this might be running on the server. Any window/document access * should start in __ready__(). * * @param {string=} childMeta The HTML generated by the child controller, if * this is a parent controller and the child generated any HTML. * @return {string} The HTML content of the head tag for this page. */ __meta__: function(childMeta) { return childMeta; }, /** * Returns the HTML representing this page. If this is a parent controller, * the argument will be the result of calling __body__ on the child * controller, which you can integrate into the response as appropriate. * * IMPORTANT: You will not have access to window or document at this point, * since this might be running on the server. Any window/document access * should start in __ready__(). * * @param {string=} childBody The HTML generated by the child controller, if * this is a parent controller and the child generated any HTML. * @return {string} The HTML content of the body tag for this page. */ __body__: function(childBody) { return childBody; }, /** * Called when we become alive in the browser. This method should wire up * the DOM that was rendered in __body__, handle any onload state, etc. * This is the point where you have a valid window and document. */ __ready__: function() {}, /** * Cleans up any event listeners, etc. that should be cleaned up before * we navigate away from this page (or rerender it). */ __unload__: function() {}, /** * Transform the body from whatever it is (probably HTML) to HTML. This can * be used if {__body__} returns something other than HTML, eg. a React * component, to transform the object into raw HTML the server can return. * * @param {Object} body The full body (result of recursive __body__() calls) * from this page. */ __bodyToHtml__: function(body) { return body; }, /** * Called in the browser to reload and regenerate the current page. * * First calls {__preload__} and {__load__} in proper order, then calls * {rerender} to rerender the page. */ reload: function() { // TODO(Taylor): Ability to reload the full stack including parents. this.__preload__(util.bind(function() { this.__load__.apply(this, arguments); this.rerender(); }, this)); }, /** * Called in the browser to regenerate the current page from the controller's * current state. * * Calls {recursiveUnload}, then {getFullTitle} and {renderFullBody} to * regenerate the page, then calls {recursiveReady} once the DOM is updated. */ rerender: function() { this.recursiveUnload(); document.title = this.getFullTitle(); document.getElementById('skit-controller').innerHTML = this.renderFullBody(); this.recursiveReady(); }, /** @ignore */ callEachAscending: function(attr, opt_fn) { var current = this.constructor; var fns = []; while (current) { if (current.prototype.hasOwnProperty(attr)) { fns.unshift(current.prototype[attr]); } current = current.__parent__; } iter.forEach(fns, opt_fn || function(r) { r.call(this); }, this); }, /** @ignore */ callEachDescending: function(attr) { var value = ''; var current = this.constructor; while (current) { if (current.prototype.hasOwnProperty(attr)) { value = current.prototype[attr].call(this, value); } current = current.__parent__; } return value; }, /** * Calls the full controller stack in ascending order. Calls each * controller class's __load__ method, starting with the base controller * all the way up to the current controller. */ recursiveLoad: function() { this.callEachAscending('__load__'); }, /** * Calls the full controller stack in ascending order. Calls each * controller class's __ready__ method, starting with the base controller * all the way up to the current controller. */ recursiveReady: function() { this.callEachAscending('__ready__'); }, /** * Calls the full controller stack in reverse order to unload. Calls each * controller class's __unload__ method, starting with the current controller * all the way through the topmost parent. */ recursiveUnload: function() { this.callEachDescending('__unload__'); }, /** * Calls the full controller stack in reverse order to generate the full * content of the title tag for the page. Calls each class's * __title__ method, passing the current title as an argument, starting * with the current controller all the way through the topmost parent * controller. * * @return {string} The HTML to put in the page's title. */ getFullTitle: function() { return this.callEachDescending('__title__'); }, /** * Calls the full controller stack in reverse order to generate the full * content of the head tag for the page. * * @return {string} The HTML to append to the page's head. */ getFullMeta: function() { return this.callEachDescending('__meta__'); }, /** * Calls the full controller stack in reverse order, rendering the full page * and returning the result as a string. * * @param {boolean=} opt_raw Whether to return the body without calling * __bodyToHtml__ first. * @return {string} The full body HTML for this page as a string. */ renderFullBody: function(opt_raw) { var body = this.callEachDescending('__body__'); if (!opt_raw) { body = this.__bodyToHtml__(body); } return body; } }); /** * Create a Controller class from the given object keys. If the first * parameter is another Controller class, it will become this controller's * parent, which will add it to the same lifecycle as the child, and its * lifecycle methods will be called automatically. * * @param {Function} parent The parent Controller. If the first * parameter is not a Controller, it will be used as {object}. * @param {Object} object The object whose member properties will become * the prototype members of the resulting class. */ Controller.create = function(var_args) { var args = Array.prototype.slice.apply(arguments); var object, parent; if (args.length == 2) { parent = args[0]; if (!parent.__controller__) { throw new Error('Specified parent is not a Controller subclass.'); } object = args[1]; } else { object = args[0]; } var klass = util.createClass(parent || Controller, object); klass.__controller__ = true; if (parent) { klass.__parent__ = parent; } return klass; }; module.exports = Controller; })(); return result || defined || module.exports; })).apply(this, arguments)});